// 服务器 -- 实现在线翻译功能 -- 字典

// 基于 DUP的服务器
#pragma once
#include "Common.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include <functional>
using namespace LogModel;

// 这里定义任务类型 -- 包装器
// 把你要翻译的给我 , 但是我想知道谁给我的
using func_t = std::function<std::string(const std::string&, InteAddr&)>;

const int default_fd = -1;
class UdpServer
{
public:
    UdpServer(uint16_t port , func_t func)
        : _port(port)
        , _sockfd(default_fd)
        , _isrunning(false)
        , _func(func)
    {
    }
    ~UdpServer() {}

    // 初始化
    void Init()
    {
        // 1. 创建套接字
        _sockfd = socket(AF_INET, SOCK_DGRAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "socket error";
            exit(Socket_error);
        }
        LOG(LogLevel::FATAL) << "socket success !";

        // 网络信息
        InteAddr local(_port);
        // 2 . 服务器绑定IP和端口号 , 即绑定网络信息
        int n = bind(_sockfd, local.NetAddrPtr(), local.NetAddrLen());
        if (n == 0)
        {
            LOG(LogLevel::INFO) << "bind success !";
        }
        else
        {
            LOG(LogLevel::FATAL) << "bind error !";
            exit(Bind_error);
        }
    }

    // 启动 -- 服务器就是一个死循环
    // 服务器就是用来把结果发给客户端的
    // 所以 , 你客服端要给我发英文字母吧 ?? 这样我才能处理后给你
    void Start()
    {
        _isrunning = true;
        while (_isrunning)
        {
            // 服务器用来转发结果
            // 但是我要先从网路中收到字母吧 ?? 然后让任务去处理我拿结果
            // 所以要先收到响应 , 你告诉我你要翻译什么 ?
            // 那我收到的消息哪里来的 ?? 远端啊
            // 所以 ,收到网络中的网络信息要转成本地

            // 1. 收信息
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            char buffer[1024];
            ssize_t ret = recvfrom(_sockfd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr *)&peer, &len);
            if (ret > 0)
            {
                //发 , 我要知道发给谁吧 ?? 
                //现在有了 peer 的网络信息 , 是 recvfrom 带回来的 
                InteAddr Client(peer);
                buffer[ret] = 0;

                // 2. 处理信息 -- 翻译  -- 就是任务
                std::string result = _func(buffer , Client); // 回调函数 , 就是处理翻译任务的

                // 3. 发信息 -- 我要知道给谁发  , 给远端 
                sendto(_sockfd,result.c_str(),result.size() , 0,Client.NetAddrPtr(),Client.NetAddrLen());
            }
        }

        _isrunning = false;
    }

private:
    uint16_t _port; // 端口
    int _sockfd;    // 套接字文件描述符
    bool _isrunning;

    func_t _func; // 任务 -- 就是处理翻译 
};